package com.gbl.protocols;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Scanner;

import com.gbl.protocols.signals.BreakProtocolSignal;
import com.gbl.protocols.signals.LeaveProtocolSignal;
import com.gbl.sessions.Session;
import com.gbl.utils.Message;
import com.gbl.utils.Show;

public class Protocols
{
	protected Session session;
	protected PrintWriter out;
	protected BufferedReader in;
	protected Scanner sc;
	
	public Protocols(Session _session)
	{
		this.session = _session;
		this.out = _session.getOut();
		this.in = _session.getIn();
		this.sc = _session.getSc();
	}
	
	public boolean launchProtocol(String protocol)
	{
		Show.test("Protocols.launchProtocol","protocol = "+protocol,2);	//TEST
		if(isProtocolOrder(protocol))
		{
			if(protocol.equals(Order.MIRROR_SERVER.toString()))
			{						
				Show.test("Protocols.launchProtocol","case MIRROR_SERVER",1);	//TEST
				doMirrorServerProtocol(protocol);
				return true;
			}
			else if(protocol.equals(Order.MIRROR_CLIENT.toString()))
			{
				Show.test("Protocols.launchProtocol","case MIRROR_CLIENT",1);	//TEST
				doMirrorClientProtocol(protocol);
				return true;
			}
			else if(protocol.equals(Order.SEND_CLIENT.toString()))
			{
				Show.test("Protocols.launchProtocol","case SEND_CLIENT",1);	//TEST
				doSendClientProtocol(protocol);
				return true;
			}
			else if(protocol.equals(Order.AUTHENTICATION.toString()))
			{
				Show.test("Protocols.launchProtocol","case AUTHENTICATION",1);	//TEST
				doAuthenticationProtocol(protocol);
				return true;
			}
			else
			{
				Show.test("Protocols.launchProtocol","case default",1);	//TEST
				Show.error("Protocols.launchProtocol","case default-no order known");
				return false;
			}
		}
		Show.test("Protocols.launchProtocol: isProtocolOrder=false",1);	//TEST
		return false;
	}
	
	public boolean launchProtocol(Order protocol)
	{
		return launchProtocol(protocol.toString());
	}
	
	/**
	 * an enum which define the orders passed by to the server
	 * 
	 * @see Protocols.StartResponse
	 * @see Protocols.StatusResponse
	 *
	 */
	public enum Order
	{
		SEND_CLIENT,
		MIRROR_CLIENT,
		MIRROR_SERVER,
		AUTHENTICATION,
		NONCE,
		DEFAULT;
	}
	
	/**
	 * an enum which define the reponses status, passed by to the server
	 * 
	 * @see Protocols.Order
	 * @see Protocols.StatusResponse
	 *
	 */
	public enum StartResponse
	{
		START_SEND_CLIENT,
		START_MIRROR_CLIENT,
		START_MIRROR_SERVER,
		START_AUTHENTICATION;	
	}
	
	/**
	 * an enum which define the reponses to start protocols, passed by to the server
	 * 
	 * @see Protocols.Order
	 * @see Protocols.StartResponse
	 *
	 */
	public enum StatusResponse
	{
		GO_AHEAD,
		FAILED,
		BREAK,
		READY,
		RETRY,
		VALIDATED;
	}
	
	/**
	 * returns whether a string is a protocol word or not
	 * 
	 * @see Protocols#isProtocolWord(String, String)
	 * @see Protocols#isProtocolWord(String, StartResponse)
	 * @see Protocols#isProtocolWord(String, StatusResponse)
	 * @see Protocols#isProtocolWord(String, Order)
	 * @see Protocols#isProtocolOrder(Order)
	 * @see Protocols#isProtocolOrder(String, Order)
	 * @see Protocols#isProtocolStatus(String)
	 * @see Protocols#isProtocolStatus(String, StatusResponse)
	 * @see Protocols#isProtocolStatus(String, String)
	 * @see Protocols#isProtocolStartResponse(String)
	 * @see Protocols#isProtocolStartResponse(String, StartResponse)
	 * @see Protocols#isProtocolStartResponse(String, String)
	 * 
	 * @param str a string to test
	 * @return a boolean wether the string is a protocol word or not
	 */
	public static boolean isProtocolWord(String str)
	{
		if(isProtocolOrder(str))
			return true;

		else if(isProtocolStatus(str))
			return true;
		
		else if(isProtocolStartResponse(str))
			return true;
		
		else
			return false;
	}
	
	/**
	 * returns whether a string is the protocol word passed on parameter or not
	 * 
	 * @see Protocols#isProtocolWord(String)
	 * @see Protocols#isProtocolWord(String, StartResponse)
	 * @see Protocols#isProtocolWord(String, StatusResponse)
	 * @see Protocols#isProtocolWord(String, Order)
	 * @see Protocols#isProtocolOrder(Order)
	 * @see Protocols#isProtocolOrder(String, Order)
	 * @see Protocols#isProtocolStatus(String)
	 * @see Protocols#isProtocolStatus(String, StatusResponse)
	 * @see Protocols#isProtocolStatus(String, String)
	 * @see Protocols#isProtocolStartResponse(String)
	 * @see Protocols#isProtocolStartResponse(String, StartResponse)
	 * @see Protocols#isProtocolStartResponse(String, String)
	 * 
	 * @param str a string
	 * @param protocolWord a string representing a protocol word
	 * @return the protocol word if it is a protocol word
	 */
	public static String isProtocolWord(String str, String protocolWord)
	{
		if(isProtocolOrder(str))
		{
			if(isProtocolOrder(str,protocolWord))
				return protocolWord;
			else
				return null;
		}
		else if(isProtocolStatus(str))
		{
			if(isProtocolStatus(str,protocolWord))
				return protocolWord;
			else
				return null;
		}
		else if(isProtocolStartResponse(str))
		{
			if(isProtocolStartResponse(str,protocolWord))
				return protocolWord;
			else
				return null;
		}
		else
			return null;
	}
	
	/**
	 * returns whether a string is the protocol word passed on parameter or not
	 * 
	 * @see Protocols#isProtocolWord(String)
	 * @see Protocols#isProtocolWord(String, StartResponse)
	 * @see Protocols#isProtocolWord(String, StatusResponse)
	 * @see Protocols#isProtocolWord(String, Order)
	 * @see Protocols#isProtocolOrder(Order)
	 * @see Protocols#isProtocolOrder(String, Order)
	 * @see Protocols#isProtocolStatus(String)
	 * @see Protocols#isProtocolStatus(String, StatusResponse)
	 * @see Protocols#isProtocolStatus(String, String)
	 * @see Protocols#isProtocolStartResponse(String)
	 * @see Protocols#isProtocolStartResponse(String, StartResponse)
	 * @see Protocols#isProtocolStartResponse(String, String)
	 * 
	 * @param str a string
	 * @param protocolWord a string representing a protocol word
	 * @return the protocol word if it is a protocol word
	 */
	public static String isProtocolWord(String str, Order protocolWord)
	{
		return isProtocolWord(str,protocolWord.toString());
	}
	
	/**
	 * returns whether a string is the protocol word passed on parameter or not
	 * 
	 * @see Protocols#isProtocolWord(String)
	 * @see Protocols#isProtocolWord(String, StartResponse)
	 * @see Protocols#isProtocolWord(String, StatusResponse)
	 * @see Protocols#isProtocolWord(String, Order)
	 * @see Protocols#isProtocolOrder(Order)
	 * @see Protocols#isProtocolOrder(String, Order)
	 * @see Protocols#isProtocolStatus(String)
	 * @see Protocols#isProtocolStatus(String, StatusResponse)
	 * @see Protocols#isProtocolStatus(String, String)
	 * @see Protocols#isProtocolStartResponse(String)
	 * @see Protocols#isProtocolStartResponse(String, StartResponse)
	 * @see Protocols#isProtocolStartResponse(String, String)
	 * 
	 * @param str a string
	 * @param protocolWord a string representing a protocol word
	 * @return the protocol word if it is a protocol word
	 */
	public static String isProtocolWord(String str, StatusResponse protocolWord)
	{
		return isProtocolWord(str,protocolWord.toString());
	}
	
	/**
	 * returns whether a string is the protocol word passed on parameter or not
	 * 
	 * @see Protocols#isProtocolWord(String)
	 * @see Protocols#isProtocolWord(String, StartResponse)
	 * @see Protocols#isProtocolWord(String, StatusResponse)
	 * @see Protocols#isProtocolWord(String, Order)
	 * @see Protocols#isProtocolOrder(Order)
	 * @see Protocols#isProtocolOrder(String, Order)
	 * @see Protocols#isProtocolStatus(String)
	 * @see Protocols#isProtocolStatus(String, StatusResponse)
	 * @see Protocols#isProtocolStatus(String, String)
	 * @see Protocols#isProtocolStartResponse(String)
	 * @see Protocols#isProtocolStartResponse(String, StartResponse)
	 * @see Protocols#isProtocolStartResponse(String, String)
	 * 
	 * @param str a string
	 * @param protocolWord a string representing a protocol word
	 * @return the protocol word if it is a protocol word
	 */
	public static String isProtocolWord(String str, StartResponse protocolWord)
	{
		return isProtocolWord(str,protocolWord.toString());
	}
	
	/**
	 * returns whether a string is a protocol order or not
	 * 
	 * @see Protocols#isProtocolOrder(String, Order)
	 * @see Protocols#isProtocolOrder(String, String)
	 * @see Protocols#isProtocolStatus(String)
	 * @see Protocols#isProtocolStatus(String, StatusResponse)
	 * @see Protocols#isProtocolStatus(String, String)
	 * @see Protocols#isProtocolStartResponse(String)
	 * @see Protocols#isProtocolStartResponse(String, StartResponse)
	 * @see Protocols#isProtocolStartResponse(String, String)
	 * 
	 * @param str a String
	 * @return a boolean
	 */
	public static boolean isProtocolOrder(String str)
	{
		for(Order ord : Order.values())
		{
			Show.test("Protocols.isProtocolOrder","values: "+ord.toString(),3);	//TEST
			if(ord.toString().equals(str))
			{
				Show.test("Protocols.isProtocolOrder","case true",3);	//TEST
				return true;
			}
		}
		Show.test("Protocols.isProtocolOrder","case false",3);	//TEST
		return false;
	}
	
	/**
	 * returns whether a string is the protocol status passed on parameter or not
	 * 
	 * @see Protocols#isProtocolOrder(Order)
	 * @see Protocols#isProtocolOrder(String, String)
	 * @see Protocols#isProtocolStatus(String)
	 * @see Protocols#isProtocolStatus(String, StatusResponse)
	 * @see Protocols#isProtocolStatus(String, String)
	 * @see Protocols#isProtocolStartResponse(String)
	 * @see Protocols#isProtocolStartResponse(String, StartResponse)
	 * @see Protocols#isProtocolStartResponse(String, String)
	 * 
	 * @param str a String
	 * @param ord Order
	 * @return a boolean
	 */
	public static boolean isProtocolOrder(String str, Order ord)
	{
		if(str.equals(ord.toString()))
		{
			Show.test("Protocols.isProtocolOrder",ord+", case true",3);	//TEST
			return true;
		}
		else
		{
			Show.test("Protocols.isProtocolOrder",ord+", case false",3);	//TEST
			return false;
		}
	}
	
	/**
	 * returns whether a string is the protocol status passed on parameter or not
	 * 
	 * @see Protocols#isProtocolOrder(Order)
	 * @see Protocols#isProtocolOrder(String, Order)
	 * @see Protocols#isProtocolStatus(String)
	 * @see Protocols#isProtocolStatus(String, StatusResponse)
	 * @see Protocols#isProtocolStatus(String, String)
	 * @see Protocols#isProtocolStartResponse(String)
	 * @see Protocols#isProtocolStartResponse(String, StartResponse)
	 * @see Protocols#isProtocolStartResponse(String, String)
	 * 
	 * @param str a String
	 * @param ord Order
	 * @return a boolean
	 */
	public static boolean isProtocolOrder(String str, String ord)
	{
		if(str.equals(ord))
		{
			Show.test("Protocols.isProtocolOrder",ord+", case true",3);	//TEST
			return true;
		}
		else
		{
			Show.test("Protocols.isProtocolOrder",ord+", case false",3);	//TEST
			return false;
		}
	}
	
	/**
	 * returns whether a string is a protocol status or not
	 * 
	 * @see Protocols#isProtocolOrder(Order)
	 * @see Protocols#isProtocolOrder(String, Order)
	 * @see Protocols#isProtocolOrder(String, String)
	 * @see Protocols#isProtocolStatus(String, StatusResponse)
	 * @see Protocols#isProtocolStatus(String, String)
	 * @see Protocols#isProtocolStartResponse(String)
	 * @see Protocols#isProtocolStartResponse(String, StartResponse)
	 * @see Protocols#isProtocolStartResponse(String, String)
	 * 
	 * @param str a String
	 * @return a boolean
	 */
	public static boolean isProtocolStatus(String str)
	{
		for(StatusResponse stat : StatusResponse.values())
		{
			Show.test("Protocols.isProtocolStatus","values: "+stat.toString(),3);	//TEST
			if(stat.toString().equals(str))
			{
				Show.test("Protocols.isProtocolStatus","case true",3);	//TEST
				return true;
			}
		}
		Show.test("Protocols.isProtocolStatus","case false",3);	//TEST
		return false;
	}
	
	/**
	 * returns whether a string is the protocol status passed on parameter or not
	 * 
	 * @see Protocols#isProtocolOrder(Order)
	 * @see Protocols#isProtocolOrder(String, Order)
	 * @see Protocols#isProtocolOrder(String, String)
	 * @see Protocols#isProtocolStatus(String)
	 * @see Protocols#isProtocolStatus(String, String)
	 * @see Protocols#isProtocolStartResponse(String)
	 * @see Protocols#isProtocolStartResponse(String, StartResponse)
	 * @see Protocols#isProtocolStartResponse(String, String)
	 * 
	 * @param str a String
	 * @param rep StatusResponse
	 * @return a boolean
	 */
	public static boolean isProtocolStatus(String str, StatusResponse rep)
	{
		if(str.equals(rep.toString()))
		{
			Show.test("Protocols.isProtocolStatus",rep+", case true",3);	//TEST
			return true;
		}
		else
		{
			Show.test("Protocols.isProtocolStatus",rep+", case false",3);	//TEST
			return false;
		}
	}
	
	/**
	 * returns whether a string is the protocol status passed on parameter or not
	 * 
	 * @see Protocols#isProtocolOrder(Order)
	 * @see Protocols#isProtocolOrder(String, Order)
	 * @see Protocols#isProtocolOrder(String, String)
	 * @see Protocols#isProtocolStatus(String)
	 * @see Protocols#isProtocolStatus(String, StatusResponse)
	 * @see Protocols#isProtocolStartResponse(String)
	 * @see Protocols#isProtocolStartResponse(String, StartResponse)
	 * @see Protocols#isProtocolStartResponse(String, String)
	 * 
	 * @param str a String
	 * @param rep StatusResponse
	 * @return a boolean
	 */
	public static boolean isProtocolStatus(String str, String rep)
	{
		if(str.equals(rep))
		{
			Show.test("Protocols.isProtocolStatus",rep+", case true",3);	//TEST
			return true;
		}
		else
		{
			Show.test("Protocols.isProtocolStatus",rep+", case false",3);	//TEST
			return false;
		}
	}
	
	/**
	 * returns whether a string is a protocol start response or not
	 * 
	 * @see Protocols#isProtocolOrder(Order)
	 * @see Protocols#isProtocolOrder(String, Order)
	 * @see Protocols#isProtocolOrder(String, String)
	 * @see Protocols#isProtocolStatus(String)
	 * @see Protocols#isProtocolStatus(String, StatusResponse)
	 * @see Protocols#isProtocolStatus(String, String)
	 * @see Protocols#isProtocolStartResponse(String, StartResponse)
	 * @see Protocols#isProtocolStartResponse(String, String)
	 * 
	 * @param str a String
	 * @return a boolean
	 */
	public static boolean isProtocolStartResponse(String str)
	{
		for(StartResponse starter : StartResponse.values())
		{
			Show.test("Protocols.isProtocolStartResponse","values: "+starter.toString(),3);	//TEST
			if(starter.toString().equals(str))
			{
				Show.test("Protocols.isProtocolStartResponse","case true",3);	//TEST
				return true;
			}
		}
		Show.test("Protocols.isProtocolStartResponse","case false",3);	//TEST
		return false;
	}
	
	/**
	 * returns whether a string is the protocol start response passed on parameter or not
	 * 
	 * @see Protocols#isProtocolOrder(Order)
	 * @see Protocols#isProtocolOrder(String, Order)
	 * @see Protocols#isProtocolOrder(String, String)
	 * @see Protocols#isProtocolStatus(String)
	 * @see Protocols#isProtocolStatus(String, StatusResponse)
	 * @see Protocols#isProtocolStatus(String, String)
	 * @see Protocols#isProtocolStartResponse(String)
	 * @see Protocols#isProtocolStartResponse(String, String)
	 * 
	 * @param str a String
	 * @param srep StartResponse
	 * @return a boolean
	 */
	public static boolean isProtocolStartResponse(String str, StartResponse srep)
	{
		if(str.equals(srep.toString()))
		{
			Show.test("Protocols.isProtocolStartResponse",srep+", case true",3);	//TEST
			return true;
		}
		else
		{
			Show.test("Protocols.isProtocolStartResponse",srep+", case false",3);	//TEST
			return false;
		}
	}
	
	/**
	 * returns whether a string is the protocol start response passed on parameter or not
	 * 
	 * @see Protocols#isProtocolOrder(Order)
	 * @see Protocols#isProtocolOrder(String, Order)
	 * @see Protocols#isProtocolOrder(String, String)
	 * @see Protocols#isProtocolStatus(String)
	 * @see Protocols#isProtocolStatus(String, StatusResponse)
	 * @see Protocols#isProtocolStatus(String, String)
	 * @see Protocols#isProtocolStartResponse(String)
	 * @see Protocols#isProtocolStartResponse(String, StartResponse)
	 * 
	 * @param str a String
	 * @param srep StartResponse
	 * @return a boolean
	 */
	public static boolean isProtocolStartResponse(String str, String srep)
	{
		if(str.equals(srep))
		{
			Show.test("Protocols.isProtocolStartResponse",srep+", case true",3);	//TEST
			return true;
		}
		else
		{
			Show.test("Protocols.isProtocolStartResponse",srep+", case false",3);	//TEST
			return false;
		}
	}
	
	protected void doMirrorClientProtocol(String str) 	{ }
	protected void doSendClientProtocol(String str) 	{ }
	protected void doMirrorServerProtocol(String str) 	{ }
	protected void doAuthenticationProtocol(String str) { }
	
	
	public static String getProtocolWord(BufferedReader in, Session session)
	{
		try
		{
			Message protocol = Message.receiveStream(in,session);
			if(protocol.getText() != null)
			{
				Show.test("Protocols.getProtocolWord",protocol.getText(),3);
				if(isProtocolWord(protocol.getText(), StatusResponse.BREAK) == null)
				{
					Show.log(">>",protocol.getText());
					return protocol.getText();
				}
				else
				{
					Show.test("Protocols.getProtocolWord","SENDING BREAK signal",2);
					throw new BreakProtocolSignal();
				}
			}
			else
			{
				Show.log("Protocols.getProtocolWord","Stream give 'null'");
				return null;
			}		
		} catch (IOException e)
		{
			Show.error("Protocols.getProtocolWord","failed to read entry: "+e.getMessage());
			return null;
		}
	}
	
	/**
	 * send a 'BREAK' status response
	 * 
	 * @see Protocols#sendFail(PrintWriter)
	 * @param out the socket printwrinter
	 */
	public static void interruptProtocol(PrintWriter out)
	{
		out.println(StatusResponse.BREAK.toString());
		out.flush();
	}
	
	/**
	 * send a 'FAILED' status response, throws a 'LeaveProtocolSignal'
	 * 
	 * @see Protocols#interruptProtocol(PrintWriter)
	 * @param out the socket printwrinter
	 * @throws LeaveProtocolSignal
	 */
	public static void sendFail(PrintWriter out)
	{
		Message.sendStream(Message.createServerMessage(StatusResponse.FAILED.toString()), out);
//		out.println(StatusResponse.FAILED.toString());
//		out.flush();
		throw new LeaveProtocolSignal();
	}
	
}